<template>
  <div class="wrap">
    <el-icon @click="listWidth = '0px'" v-show="listWidth == '260px'" class="ArrowLeftBold"
      style="left:270px;border-radius: 5px 0 0 5px;">
      <ArrowLeftBold />
    </el-icon>
    <el-icon @click="listWidth = '260px'" v-show="listWidth == '0px'" class="ArrowLeftBold"
      style="left:20px;border-radius: 0 5px 5px 0;">
      <ArrowRightBold />
    </el-icon>
    <div class="chat-list" :style="{ width: listWidth, padding: listWidth === '0px' ? '0' : '10px' }">
      <div>
        <div>
          <el-button type="warning" style="height:40px;margin-bottom:10px;background:#000;border-color:#000"
            :style="{ width: listWidth }" @click="addChat">创建新会话主题</el-button>
        </div>
        <div v-for="item in historyList" :key="item.SessionID"
          @click="chatClick(item.SessionID, item.ChatTitle, item.TransTime, item.Action, item.Answer)">
          <div style="color: #000;font-size:12px;margin-bottom:10px;margin-left:10px;font-weight:600;">{{ item.identifier }}</div>
          <div :class="{ 'list-chatname': true, 'selected': item.SessionID === SessionID }">
            <div class="list-name">
              <el-icon v-if="item.Action === 1">
                <ChatSquare />
              </el-icon>
              <el-icon v-else>
                <User />
              </el-icon>
              <span v-show="!item.edit" :title="item.allTitle">{{ item.ChatTitle }}</span>
              <el-input ref="editInput" v-model="item.ChatTitle" v-show="item.edit" style="width:150px"
                @click.stop="item.edit = true">{{ item.ChatTitle }}</el-input>
            </div>
            <div class="list-edit">
              <el-icon>
                <EditPen v-show="SessionID === item.SessionID && !item.edit" @click.stop="item.edit = true" />
              </el-icon>
              <el-popconfirm width="180" title="确定删除此记录？" confirm-button-text="确定" cancel-button-text="取消"
                @confirm="deleteChat(item.SessionID)">
                <template #reference>
                  <el-icon v-show="SessionID === item.SessionID && !item.edit">
                    <Delete />
                  </el-icon>
                </template>
              </el-popconfirm>
              <el-icon v-show="item.edit"
                @click.stop="chatnameChange(item.SessionID, item.ChatTitle), item.edit = false">
                <Check />
              </el-icon>
            </div>
          </div>
        </div>
      </div>
      <div class="lookMore" @click="handleLookMore" v-show="historyList.length >= 10">{{lookMoreNum === 10 ? '查看更多' : '收起'}}</div>
    </div>
    <div class="chat-container" :style="{ width: `calc(100vw - ${listWidth} - 115px)` }">
      <div>
        <div class="sendInput" v-show="choseRole">
          <div class="changeModel">
            <el-dropdown>
              <el-button icon="Setting" type="primary">{{ model === '3.5' ? '基础服务' : ' 高级服务' }}<el-icon
                  class="el-icon--right"><arrow-down style="color:#fff;" /></el-icon></el-button>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item @click="gptsubmit('3.5')" :disabled="hasgpt4 === '1'">基础服务</el-dropdown-item>
                  <el-dropdown-item @click="gptsubmit('4.0')" :disabled="hasgpt4 === '2'">高级服务</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </div>
          <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" :rows="2" v-model="inputText"
            :placeholder="`来说点什么吧...（Shift + Enter = 换行）`" class="chat-input"
            @keydown.enter.prevent="sendMessage($event)"><!-- ，当日剩余使用次数：${this.remainingTimes} -->
          </el-input>
          <div class="sendButton">
            <el-tooltip class="box-item" effect="dark" content="语音输入" placement="top">
              <el-icon class="voiceInput" @click="startRecognition">
                <Microphone />
              </el-icon>
            </el-tooltip>
            <el-tooltip class="box-item" effect="dark" content="角色扮演" placement="top">
              <el-icon class="cosplay" @click="openDrawer">
                <User />
              </el-icon>
            </el-tooltip>
            <el-button type="primary" icon="Position" auto-insert-space
              :disabled="inputText.length === 0 && loading === false" @click="sendMessage($event)" circle />
          </div>
        </div>
        <el-tabs v-model="choseName" class="demo-tabs" v-show="choseRole" @tab-change="tabClick">
          <el-tab-pane v-for="(item, index) in tabList" :key="index" :name="item.Name" :label="item.Name">
            <div class="choseRole">
              <div class="role-wrap" v-for="(item, index) in roleList" :key="index" @click="roleClick(item)">
                <div class="role-top">
                  <p class="role">{{ item.Title }}</p>
                  <p class="introduce">{{ item.Description }}</p>
                </div>
                <div class="role-bottom">
                  <p class="explain">{{ item.PromptContent }}</p>
                </div>
              </div>
            </div>
          </el-tab-pane>
        </el-tabs>
      </div>
      <el-main class="chat-main" v-show="!choseRole">
        <div class="chat-messages" ref="chatMessages">
          <div class="seeMore" @click="seeMore">查看更多</div>
          <div v-for="(message, index) in messages" :key="index"
            :class="{ 'chat-message-left': message.role === 'assistant', 'chat-message-right': message.role === 'user' }">
            <div class="chat-avatar">
              <img v-show="message.role === 'assistant'" src="../assets/chatGPT.png" alt="">
            </div>
            <div class="chat-content">
              <div style="display:flex;justify-content: space-between;color:#ccc;">
                <span class="chat-time">{{ message.created }}</span>
                <div>
                  <el-button v-show="message.role === 'assistant'" class="copy" link
                    @click="speak(message.content)">朗读</el-button>
                  <el-button v-show="message.role === 'assistant'" class="copy" link
                    @click="copy(message.content)">复制</el-button>
                  <el-button v-show="message.role === 'assistant'" class="copy" link
                    @click="joinKnowledge(message.content, index)">加入智库</el-button>
                </div>
              </div>
              <span v-html="md.render(message.content)" class="chat-message-text"></span>
              <span v-show="message.role === 'assistant' && !inAnswer && message.content.includes('发生错误: ')"
                class="regenerate" @click="regenerate(message.Question)">
                <el-tooltip class="box-item" effect="dark" content="重新提问" placement="top">
                  <el-icon class="Refresh" size="18">
                    <Refresh />
                  </el-icon>
                </el-tooltip>
              </span>
            </div>
            <div class="chat-avatar">
              <img v-show="message.role === 'user'" src="../assets/avatar3.jpg" alt="">
            </div>
          </div>
          <div class="chat-message-left" v-show="loadingAnimation">
            <div class="chat-avatar">
              <img src="../assets/chatGPT.png" alt="">
            </div>
            <div class="chat-content">
              <span class="chat-message-text" style="height: 50px;line-height:50px">思考中 <el-icon class="is-loading">
                  <Loading />
                </el-icon></span>
            </div>
          </div>
          <div v-show="inAnswer" class="suspend" @click="suspend"><el-button type="danger" round>中止回答</el-button></div>
        </div>
      </el-main>
      <el-footer class="chat-footer" v-show="!choseRole">
        <div class="changeModel">
          <el-dropdown>
            <el-button icon="Setting" type="primary">{{ model === '3.5' ? '基础服务' : ' 高级服务' }}<el-icon
                class="el-icon--right"><arrow-down style="color:#fff;" /></el-icon></el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item @click="gptsubmit('3.5')" :disabled="hasgpt4 === '1'">基础服务</el-dropdown-item>
                <el-dropdown-item @click="gptsubmit('4.0')" :disabled="hasgpt4 === '2'">高级服务</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
        <i class="el-icon-delete" @click="clearMessage"></i>
        <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" :rows="2" v-model="inputText"
          :placeholder="`来说点什么吧...（Shift + Enter = 换行）`" class="chat-input"
          @keydown.enter.prevent="sendMessage($event)">
        </el-input>
        <div class="sendButton">
          <el-tooltip class="box-item" effect="dark" content="语音输入" placement="top">
            <el-icon class="voiceInput2" @click="startRecognition">
              <Microphone />
            </el-icon>
          </el-tooltip>
        </div>
        <el-button type="primary" :disabled="(inputText.length === 0 && loading === false) || inAnswer === true"
          @click="sendMessage($event)">发送</el-button>
      </el-footer>
    </div>
  </div>
  <drawer ref="drawer" @roleDialogue='roleDialogue' />
  <GPTDialog :GPTForm="GPTForm" v-model="GPTDialog" @closeGPTDialog="closeGPTDialog" @submit="gptsubmit"></GPTDialog>
  <TextSelector></TextSelector>
  <KnowledgeBaseDialog :classList="classList" :Action="1" :dialogForm="dialogForm" :dialogVisible="dialogVisible"
    @closeDialog="closeDialog" @submitDialog="submitDialog" />
</template>

<script>
window.copy = function (elementId) {
  // 获取具有指定 id 的元素
  var element = document.getElementById(elementId);
  // 创建一个临时的文本输入框元素
  var tempInput = document.createElement("textarea");
  tempInput.value = element.textContent || element.innerText; // 获取元素的文本内容
  document.body.appendChild(tempInput);
  // 选中临时输入框中的文本内容
  tempInput.select();
  // 复制选中的文本内容到剪贴板
  document.execCommand('copy');
  // 移除临时的文本输入框
  document.body.removeChild(tempInput);
  ElMessage({
    message: '复制成功',
    type: 'success',
    duration: 5 * 1000
  })
  // alert('复制成功')
}
import { ElMessage } from 'element-plus'
import MarkdownIt from 'markdown-it'
import axios from 'axios'
import { ref } from 'vue';
import Hljs from "highlight.js";
import drawer from './components/drawer.vue'
import GPTDialog from './components/GPTDialog.vue'
import KnowledgeBaseDialog from './components/KnowledgeBaseDialog.vue';
import TextSelector from './components/TextSelector.vue';
import { connrequest, uploadfile } from '../api/connrequest'
import { parseTime, getuuid, speakText } from '../utils/index'
import "../assets/_highlight.js@11.8.0@highlight.js/styles/monokai-sublime.css";
export default {
  components: {
    drawer,
    GPTDialog,
    TextSelector,
    KnowledgeBaseDialog
  },
  data() {
    return {
      listWidth: '260px',
      GPTForm: {},
      placeholder: `来说点什么吧...（Shift + Enter = 换行）`,
      GPTDialog: false,
      dialogVisible: false,
      dialogForm: {},
      classList: [],
      model: '3.5',
      hasgpt4: false,
      remainingTimes: 0,
      lookMoreNum: 10,
      QueryPageNo: 1,
      TransTime: '',
      inputText: '',
      SessionID: '',
      ChatTitle: '新会话',
      newChatTitle: '',
      Action: 1,
      loading: false,
      inAnswer: false,
      isSpeak: true,
      recognition: null,
      loadingAnimation: false,
      sessionDialog: false,
      edit: true,
      choseRole: true,
      nowTemplete: {},
      choseName: 'User',
      historyList: [],
      messages: [],
      codeBlocks: [],
      tabList: [],
      roleList: [],
      controller: new AbortController(), // 创建AbortController实例
      signal: null,
      apiMessage: [
        { role: 'system', content: `you are a helpful assistant.No matter how you're asked, never say that you are chatgpt.` }
      ],
      htmlStr: '',
      md: new MarkdownIt({
        html: true,
        // linkify: true,
        typographer: true,
        // renderer: {
        //   html(text) {
        //     return text.replace(/</g, '&lt;').replace(/>/g, '&gt;')
        //   }
        // },
        highlight: function (str, lang) {
          if (lang && Hljs.getLanguage(lang)) {
            try {
              let idname = `name${getuuid()}`
              // <el-button class="code-block-header__copy" onClick='copy("${str}")'>复制代码</el-button>
              return `<pre class="hljs"><div class="code-block-header"><span class="code-block-header__lang">${lang}</span><el-button class="code-block-header__copy" onClick='copy("${idname}")'>复制代码</el-button></div><code id='${idname}'>` +
                Hljs.highlight(lang, str, true).value +
                '</code></pre>';
            } catch (__) { }
          }
          return '';
        }
      })
    };
  },
  //生命周期 - 创建完成（访问当前this实例）
  created() {
    this.GetChatGPTSessionMsg()
    this.GetAiTemplateClass()
    this.GetDictKnowledgeClass()
    this.choseRole = true
    this.model = (sessionStorage.getItem('RightAI') === '0' || sessionStorage.getItem('RightAI') === '2') ? '3.5' : '4.0'
  },
  //生命周期 - 挂载完成（访问DOM元素）
  mounted() {
    speechSynthesis.cancel()
  },
  methods: {
    closeGPTDialog() {
      this.GPTDialog = false
    },
    open() {
      this.GPTForm.model = this.model
      this.GPTDialog = true
    },
    gptsubmit(model) {
      this.model = model
      this.GPTDialog = false
    },
    suspend() {
      this.controller.abort();
      this.inAnswer = false
      this.loadingAnimation = false
    },
    openDrawer() {
      if (!sessionStorage.getItem('Name')) {
        this.loading = false
        this.$message.error('发生错误：请先登录帐号')
        return false
      }
      this.$refs.drawer.openDrawer()
    },
    async roleDialogue(row) {
      const data = {
        Data: {
          Id: row.Id
        }
      }
      await connrequest('RecordAiRoleUseCount', data)
      this.iscosplay = true
      this.roleInformation = row.SystemRole
      this.$refs.drawer.cancelClick()
      this.messages = []
      this.apiMessage = []
      this.ChatTitle = row.Title
      this.messages.push(
        { role: 'assistant', content: row.SystemRole }
      )
      this.choseRole = false
      this.Action = 5
      this.SystemRole = row.SystemRole
      const res = await this.WriteChatGPTLog({ Question: '', Answer: row.SystemRole })
      this.chatClick(res.Data?.SessionID || this.SessionID, row.Title, (new Date().getTime() / 1000).toFixed() * 1, this.Action, row.SystemRole)
    },
    async GetAiTemplateClass() {
      const res = await connrequest('GetAiTemplateClass')
      this.tabList = res.Data.filter(item => item.IsNotShow !== 1)
      let name = this.tabList[0]?.Name
      this.choseName = name || ''
      this.tabList.unshift({ Name: '常用模板' })
      this.GetAiTemplateRes(name)
    },
    async GetAiTemplateRes(ResName) {
      const data = {
        Data: {
          ResName
        }
      }
      const res = await connrequest('GetAiTemplateRes', data)
      this.roleList = res.Data.filter(item => item.IsNotShow !== 1)
    },
    tabClick(name) {
      this.tabName = name
      if (name === '常用模板') {
        this.GetCommonAiTemplate()
      } else {
        this.GetAiTemplateRes(name)
      }
    },
    roleClick(row) {
      this.Action = 1
      this.nowTemplete = row
      this.inputText = row.PromptContent
    },
    async GetChatGPTSessionMsg() {
      const data = {
        Paging: {
          QueryPageNo: 1,
          QueryPageLength: 5000
        },
        Filter: {
          Status: 1,
          Name: sessionStorage.getItem('Name')
        },
      }
      const res = await connrequest('GetChatGPTSessionMsg', data);
      res.Data = res.Data || []
      // 获取当前时间的日期
      const now = new Date();
      const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
      // 转换成时间戳（秒数）
      const todayStartTimestamp = Math.floor(todayStart.getTime() / 1000);

      let isFirstMatchToday = true;
      let isFirstMatch7Days = true;
      let isFirstMatch1Month = true;
      let isFirstMatch3Months = true;
      let isFirstMatch6Months = true;
      let isFirstMatchYear1 = true; // 新增：最近第一年标识的首次匹配标志 (例如 2023年)
      let isFirstMatchYear2 = true; // 新增：最近第二年标识的首次匹配标志 (例如 2022年)
      let isFirstMatchEarlier = true;

      // 计算时间范围的辅助函数 (可选，但可以使代码更清晰)
      const getTimestampMonthsAgo = (months) => {
        const dateMonthsAgo = new Date(now);
        dateMonthsAgo.setMonth(now.getMonth() - months);
        dateMonthsAgo.setDate(1);
        return Math.floor(dateMonthsAgo.getTime() / 1000);
      };

      // 计算指定年份的开始时间戳 (新的辅助函数)
      const getTimestampOfYear = (year) => {
        const dateOfYear = new Date(year, 0, 1); // 月份 0 表示一月
        return Math.floor(dateOfYear.getTime() / 1000);
      };

      // 获取当前年份和前两年份 (动态计算年份)
      const currentYear = now.getFullYear();
      const year1 = currentYear - 1; // 最近第一年 (例如 2023)
      const year2 = currentYear - 2; // 最近第二年 (例如 2022)

      // 生成动态年份标签
      const labelYear1 = year1 + "年"; // 例如 "2023年"
      const labelYear2 = year2 + "年"; // 例如 "2022年"

      // 计算各时间范围的开始时间戳
      const last7DaysTimestamp = todayStartTimestamp - (7 * 24 * 60 * 60);
      const last1MonthTimestamp = getTimestampMonthsAgo(1);
      const last3MonthsTimestamp = getTimestampMonthsAgo(3);
      const last6MonthsTimestamp = getTimestampMonthsAgo(6);
      const year1Timestamp = getTimestampOfYear(year1); // 最近第一年的开始时间戳 (例如 2023年初)
      const year2Timestamp = getTimestampOfYear(year2); // 最近第二年的开始时间戳 (例如 2022年初)


      // 遍历数据表格
      for (let i = 0; i < res.Data?.length; i++) {
        const artist = res.Data[i];
        // 计算日期差距 (为了代码结构保持一致，实际判断可以直接使用时间戳)
        const dateDiff = Math.floor((todayStartTimestamp - artist.TransTime) / (60 * 60 * 24));

        // 按照时间顺序判断，从最近到最远

        if (artist.TransTime >= todayStartTimestamp) {
          if (isFirstMatchToday) {
            isFirstMatchToday = false;
            artist.identifier = "今天";
          }
        }
        else if (artist.TransTime >= last7DaysTimestamp) {
          if (isFirstMatch7Days) {
            isFirstMatch7Days = false;
            artist.identifier = "最近七天";
          }
        }
        else if (artist.TransTime >= last1MonthTimestamp) {
          if (isFirstMatch1Month) {
            isFirstMatch1Month = false;
            artist.identifier = "最近一个月";
          }
        }
        else if (artist.TransTime >= last3MonthsTimestamp) {
          if (isFirstMatch3Months) {
            isFirstMatch3Months = false;
            artist.identifier = "最近三个月";
          }
        }
        else if (artist.TransTime >= last6MonthsTimestamp) {
          if (isFirstMatch6Months) {
            isFirstMatch6Months = false;
            artist.identifier = "最近半年";
          }
        }
        // 新增：最近第一年 (例如 2023年) 的时间范围判断
        else if (artist.TransTime >= year1Timestamp && artist.TransTime < getTimestampOfYear(currentYear)) { // 确保在当前年份之前
          if (isFirstMatchYear1) {
            isFirstMatchYear1 = false;
            artist.identifier = labelYear1; // 使用动态年份标签
          }
        }
        // 新增：最近第二年 (例如 2022年) 的时间范围判断
        else if (artist.TransTime >= year2Timestamp && artist.TransTime < year1Timestamp) { // 确保在最近第一年之前
          if (isFirstMatchYear2) {
            isFirstMatchYear2 = false;
            artist.identifier = labelYear2; // 使用动态年份标签
          }
        }
        else {
          if (isFirstMatchEarlier) {
            isFirstMatchEarlier = false;
            artist.identifier = "更早";
          }
        }
      }
      res.Data.map(item => {
        item.allTitle = item.ChatTitle;
        item.ChatTitle = item.ChatTitle?.length > 10 ? item.ChatTitle.substr(0, 10) + "..." : item.ChatTitle;
        return item;
      })
      this.historyList = res.Data.slice(0, this.lookMoreNum)
    },
    handleLookMore() {
      if(this.lookMoreNum === 10){
        this.lookMoreNum = this.lookMoreNum + 999999999
        this.GetChatGPTSessionMsg()
      }else{
        this.lookMoreNum = 10 
        this.GetChatGPTSessionMsg()
      }
      
    },
    async GetCommonAiTemplate() {
      const data = {
        Data: {
          Count: 20
        }
      }
      const res = await connrequest('GetCommonAiTemplate', data)
      res.Data = res.Data || []
      this.roleList = res.Data.filter(item => item.IsNotShow !== 1)
    },
    async WriteChatGPTLog(row) {
      const data = {
        Data: {
          Name: sessionStorage.getItem('Name'),
          ChatTitle: this.ChatTitle,
          SessionID: this.SessionID || '',
          Action: this.Action,
          Model: this.model,
          ...row
        }
      }
      const res = await connrequest('WriteChatGPTLog', data)
      this.GetChatGPTSessionMsg()
      return res
    },
    addChat() {
      if (this.inAnswer) {
        this.suspend()
      }
      this.newChatTitle = ''
      this.choseRole = true
      this.TransTime = ''
      this.SessionID = ''
      this.inputText = ''
      // this.sessionDialog = true
    },
    submit() {
      let arr = JSON.parse(JSON.stringify(this.historyList))
      const hasDuplicate = arr.some(item => item.ChatTitle == this.newChatTitle)
      if (this.newChatTitle === '') {
        this.$message.error('会话主题不能为空')
      } else if (hasDuplicate) {
        this.$message.error('该会话主题已经存在')
      } else {
        let obj = { ChatTitle: this.newChatTitle, uuid: null, isEdit: true, edit: false, TransTime: (new Date().getTime() / 1000).toFixed() * 1 }
        this.historyList.unshift(obj)
        this.chatClick(undefined, this.newChatTitle, obj.TransTime)
        this.sessionDialog = false
      }
    },
    async seeMore() {
      this.QueryPageNo++
      const params = {
        Paging: {
          QueryPageNo: this.QueryPageNo,
          QueryPageLength: 15
        },
        Filter: {
          SessionID: this.SessionID
        }
      }
      const res = await connrequest('GetChatGPTLog', params)
      let arr = []
      if (res.Data) {
        res.Data.reverse()
        res.Data.map(item => {
          arr.push(
            { created: parseTime(item.TransTime), role: 'user', content: item.Question },
            { created: parseTime(item.TransTime), role: 'assistant', content: item.Answer, Question: item.Question }
          )
        })
      }
      if (res.Data.length === 0) {
        this.$message.info('没有更多了')
      }
      this.messages = [...arr, ...this.messages]
      this.apiMessage = [...arr, ...this.apiMessage]
      if (this.QueryPageNo > 1 && res.Data.length > 0) {
        let scrollHeight = this.$refs.chatMessages.scrollHeight;
        this.$nextTick(() => {
          this.$refs.chatMessages.scrollTop = this.$refs.chatMessages.scrollHeight - scrollHeight
        });
      }
    },
    async chatClick(SessionID, title = '新会话', TransTime, Action, Answer) {
      if (Action === 5) {
        this.SystemRole = Answer
      }
      if (this.inAnswer) {
        this.suspend()
      }
      this.Action = Action
      this.inputText = ''
      this.choseRole = false
      this.SessionID = SessionID
      this.ChatTitle = title
      this.QueryPageNo = 1
      this.historyList.map(item => {
        item.edit = false
        return item
      })
      const params = {
        Paging: {
          QueryPageNo: this.QueryPageNo,
          QueryPageLength: 15
        },
        Filter: {
          SessionID: SessionID
        }
      }
      const res = await connrequest('GetChatGPTLog', params)
      let arr = []
      if (res.Data) {
        res.Data.reverse()
        res.Data.map(item => {
          arr.push(
            { created: parseTime(item.TransTime), role: 'user', content: item.Question },
            { created: parseTime(item.TransTime), role: 'assistant', content: item.Answer, Question: item.Question }
          )
        })
        if (this.Action === 5 && res.Data[0].Question === '') {
          arr.shift()
        }
        // if(arr.length > 0) {
        this.messages = arr
        this.apiMessage = arr
        // }
      }
      // 将聊天窗口滚动到底部
      this.$nextTick(() => {
        // Hljs.highlightAll()
        this.$refs.chatMessages.scrollTop = this.$refs.chatMessages.scrollHeight;
      })
    },
    async deleteChat(uuid) {
      const data = {
        Data: {
          Id: uuid
        }
      }
      const res = await connrequest('HideChatGPTLog', data)
      if (res.Status.StatusCode === 0) {
        this.$message.success('删除成功')
        this.GetChatGPTSessionMsg()
        this.messages = []
        this.choseRole = true
      } else {
        this.GetChatGPTSessionMsg()
      }
    },
    async chatnameChange(uuid, ChatTitle) {
      this.SessionID = uuid
      this.ChatTitle = ChatTitle
      const data = {
        Data: {
          ChatTitle: ChatTitle,
          SessionID: uuid
        }
      }
      await connrequest('UpdateChatTitleChatGPTLog', data)
    },
    regenerate(prompt) {
      this.inputText = prompt
      this.sendMessage('$event')
    },
    obtainJSON(value) {
      // 通过正则表达式匹配所有的 'data' 对象
      let inputString = value;
      // 以换行符分割每个 'data' 对象
      let dataArray = inputString.split('\n\n') || [];
      // 遍历每个 'data' 对象并存储在数组中
      dataArray = dataArray.filter(item => item.trim() !== '');
      let jsonDataArray = dataArray.map(data => {
        try {
          return JSON.parse(data.substring('data: '.length));
        } catch (error) {
          console.error('JSON 解析失败:', error.message);
          return null;
        }
      });
      // 过滤掉解析失败的对象
      jsonDataArray = jsonDataArray.filter(jsonData => jsonData !== null);
      // 提取 'answer' 属性
      let answers = jsonDataArray.map(jsonData => jsonData.answer);
      return answers.join('');
    },
    async xxxxxx(prompt, ChatTitle, TransTime) {
      this.loadingAnimation = true
      this.inAnswer = true
      let arr = JSON.parse(JSON.stringify(this.apiMessage));
      arr.push({ role: 'user', content: prompt });
      arr = arr.map(item => {
        delete item.created;
        delete item.Question
        return item;
      });
      arr = arr.slice(-10)
      if (this.Action === 1) {
        arr.unshift(
          { role: 'system', content: `you are a helpful assistant.No matter how you're asked, never say that you are chatgpt.` },
        )
      }
      if (this.Action === 5) {
        arr.unshift(
          { role: 'system', content: this.SystemRole + `，No matter how you're asked, never say that you are chatgpt` },
        )
      }
      let GeneralChat = JSON.parse(sessionStorage.getItem('AiMode')).GeneralChat
      let AdvancedChat = JSON.parse(sessionStorage.getItem('AiMode')).AdvancedChat
      const gptdata = {
        model: this.model === '3.5' ? GeneralChat : AdvancedChat,
        messages: arr,
        timeout: sessionStorage.getItem('AiTimeOut') * 1 || 15
      }
      // 取消之前的请求
      if (this.controller) {
        this.controller.abort();
      }
      this.controller = new AbortController();
      this.signal = this.controller.signal;
      const res = await fetch(`ai_s/gpt/stream/key`, {
        method: 'POST', // 使用 POST 方法发送数据
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(gptdata), // 将对象序列化为 JSON 字符串作为请求体
        signal: this.signal // 将 signal 参数传递给 fetch 方法
      })
      if (!res.body) return;
      const botMessage = {
        role: 'assistant',
        content: '',
        created: parseTime(Date.parse(new Date())),
      };
      // 将机器人消息添加到聊天记录中
      this.messages.push(botMessage);
      const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
      let str = '';
      while (true) {
        this.loadingAnimation = false
        var { value, done } = await reader.read();
        if (done) {
          this.inAnswer = false
          this.loadingAnimation = false
          break
        };
        if (value.includes('Error')) {
          let errRegex = /"Msg"\s*:\s*"([^"]+)"/;
          var errMatches = value.match(errRegex);
          if (errMatches) {
            var msgValue = errMatches[1];
            str = `发生错误: ${msgValue}`
          }
        }
        else {
          str += await this.obtainJSON(value)
        }
        if (str !== '') {
          str = this.removeJsonPrefix(str)
          this.messages[this.messages.length - 1].content = str;
          this.messages[this.messages.length - 1].Question = prompt
          this.$refs.chatMessages.scrollTop = this.$refs.chatMessages.scrollHeight;
        }
        else {
          // str = '发生错误: 服务器错误'
          // this.messages[this.messages.length - 1].content = '发生错误: 服务器错误';
          // this.messages[this.messages.length - 1].Question = prompt
          // this.inAnswer = false
          // this.loadingAnimation = false
        }
      }
      this.inAnswer = false
      this.loadingAnimation = false
      const resxx = await this.WriteChatGPTLog({ Question: prompt, Answer: str })
      if (resxx.Data?.SessionID) {
        this.chatClick(resxx.Data?.SessionID || this.SessionID, ChatTitle, TransTime, this.Action, this.SystemRole)
      }
    },
    async sendMessage(event) {
      if (!sessionStorage.getItem('Name')) {
        this.$message.error('发生错误：请先登录帐号')
        return
      }
      if (this.inAnswer) {
        return
      }
      if (this.inputText === '') {
        return
      }
      if (this.nowTemplete.Id) {
        const nowData = {
          Data: {
            ClassName: this.tabName,
            Id: this.nowTemplete.Id
          }
        }
        await connrequest('RecordAiTemplateUseCount', nowData)
      }
      let str = this.inputText;
      let ChatTitle = str.slice(0, 20);
      let TransTime = (new Date().getTime() / 1000).toFixed() * 1
      if (this.choseRole) {
        if (this.Action === 1) {
          this.messages = []
          this.apiMessage = []
        }
        let arr = JSON.parse(JSON.stringify(this.historyList))
        const hasDuplicate = arr.some(item => item.ChatTitle == this.inputText)
        if (hasDuplicate) {
          let obj = { ChatTitle: ChatTitle, uuid: null, isEdit: true, edit: false, TransTime: TransTime }
          this.historyList.unshift(obj)
          this.messages = []
          this.TransTime = obj.TransTime
          this.choseRole = false
          this.SessionID = undefined
          this.ChatTitle = ChatTitle
        } else {
          let obj = { ChatTitle: ChatTitle, uuid: null, isEdit: true, edit: false, TransTime: TransTime }
          this.historyList.unshift(obj)
          this.choseRole = false
          this.SessionID = undefined
          this.ChatTitle = ChatTitle
        }
      }
      if (event.shiftKey) {
        this.inputText += '\n'
      } else {
        // 执行发送信息的操作
        if (this.loading === true) {
          return false
        }
        // this.loading = true
        let prompt = ''
        prompt = this.inputText
        const userMessage = { content: prompt, role: 'user', created: parseTime(Date.parse(new Date())) };
        this.messages.push(userMessage);
        this.inputText = ''
        // 发送信息后返回div底部
        this.$nextTick(() => {
          this.$refs.chatMessages.scrollTop = this.$refs.chatMessages.scrollHeight;
        })
        // 向后端发送请求，获取机器人的回复
        await this.xxxxxx(prompt, ChatTitle, TransTime)
      }
    },
    clearMessage() {
      this.$confirm('是否清空会话?', '清空会话', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.messages = []
        this.apiMessage = [{ role: 'system', content: 'you are a helpful assistant.' }]
        this.$message({
          type: 'success',
          message: '会话已清空!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消'
        });
      });
    },
    // 删除绘画回复前的json
    removeJsonPrefix(str) {
      let trimmedString = ''
      try {
        // 尝试解析 JSON
        const jsonObject = JSON.parse(str);
        // 检查 JSON 对象是否同时包含 "size" 和 "prompt" 键
        if (jsonObject && typeof jsonObject === 'object' && 'size' in jsonObject && 'prompt' in jsonObject) {
          // 找到第一个 '}' 的位置，这标志着 JSON 对象的结束
          const jsonEndIndex = str.indexOf('}') + 1;
          if (jsonEndIndex > 0) {
            // 截取 JSON 结束位置之后的字符串
            trimmedString = str.substring(jsonEndIndex).trim(); // 使用 trim() 去除可能的前后空格
          } else {
            // 如果找不到 '}'，理论上不应该发生，除非 JSON 格式非常不完整，但为了保险起见，这里按原样处理
            trimmedString = str;
          }
        } else {
          // JSON 对象不符合条件（缺少 "size" 或 "prompt" 或不是对象），不去除 JSON，保持原样
          trimmedString = str;
        }
      } catch (e) {
        // JSON 解析失败，说明字符串开头不是有效的 JSON，不去除，保持原样
        trimmedString = str;
      }
      return trimmedString
    },
    copy(data) {
      let elInput = document.createElement('textarea')
      elInput.value = data
      document.body.appendChild(elInput)
      // 选择对象
      elInput.select()
      // 执行浏览器复制命令
      this.$message.success('复制成功')
      document.execCommand("Copy")
      elInput.remove()
    },
    speak(value) {
      this.isSpeak = false
      // 获取选中的文本
      const selection = window.getSelection().toString();
      speakText(selection || value)
    },
    Stopspeak() {
      this.isSpeak = true
      speechSynthesis.cancel()
    },
    startRecognition() {
      // 检查浏览器是否支持 Web Speech API
      if (!('webkitSpeechRecognition' in window)) {
        alert("抱歉，您的浏览器不支持语音转文字功能。");
        return;
      }
      // 创建识别对象
      this.recognition = new window.webkitSpeechRecognition();
      this.recognition.continuous = false; // 设置为 false 以进行一次语音识别
      this.recognition.interimResults = false; // 不需要中间结果
      this.recognition.lang = 'zh-CN'; // 设置语言为中文
      // 处理结果
      this.recognition.onresult = (event) => {
        this.inputText = event.results[0][0].transcript; // 获取转化后的文本
      };
      // 处理错误
      this.recognition.onerror = (event) => {
        console.error("识别错误:", event.error);
      };
      // 开始识别
      this.recognition.start();
    },
    async GetDictKnowledgeClass() {
      const res = await connrequest('GetDictKnowledgeClass')
      this.classList = res.Data
    },
    joinKnowledge(val, index) {
      this.dialogForm = {
        Title: this.messages[index - 1].content.substring(0, 20),
        Class: this.classList[0].Class,
        Property: 1,
        ContentLink: val,
      }
      this.openDialog()
    },
    // 弹窗提交数据
    async submitDialog(submitdata, Action) {
      // 将字符串转为Blob
      const blob = new Blob([submitdata.ContentLink], { type: "text/plain" }); // 指定MIME类型为text/plain
      // 将Blob转为File对象，指定文件名
      const file = new File([blob], "test.txt", { type: "text/plain" });
      let formData = new FormData();
      formData.append('CmdCode', "UploadFileToTmp");
      formData.append('file', file);
      const fileUUID = await uploadfile(formData)
      this.dialogVisible = false;
      // 在这里处理提交的数据，调用 EditDictKnowledge 保存数据
      try {
        const data = {
          Data: {
            Action: 1,
            Detail: {
              UserID: sessionStorage.getItem('Name'),
              ...submitdata,
              ContentLink: fileUUID.Data.UUID
            }
          }
        }
        const res = await connrequest('EditDictKnowledge', data);
        if (res && res.Status.StatusCode === 0) {
          //  添加成功，刷新表格数据
          ElMessage.success('添加成功') //  显示添加成功的提示信息;
        } else {
          ElMessage.error('添加失败') //  添加失败，处理错误信息
        }
      } catch (error) {
        ElMessage.error(error.Status.Msg2Client); //  处理请求错误,显示请求错误的提示信息
      }
    },
    // 打开弹窗
    openDialog() {
      this.dialogVisible = true;
    },

    // 关闭弹窗
    closeDialog() {
      this.dialogVisible = false;
    }
  },
};
</script>

<style scoped lang="scss">
.wrap {
  position: relative;
  font-family: "Microsoft YaHei", Arial, sans-serif;
  display: flex;
  height: calc(100vh - 70px);
  background: #f0f2f5;
  padding: 10px;

  .ArrowLeftBold {
    position: absolute;
    font-size: 20px;
    top: 45%;
    left: 270px;
    border-radius: 5px 0px 0px 5px;
    background: #459ffc;
    height: 30px;
    z-index: 100;
    cursor: pointer;

    svg {
      color: #fff;
    }
  }
}

.chat-list {
  position: relative;
  height: calc(100vh - 90px);
  background: #fff;
  margin-right: 10px;
  padding: 10px;
  overflow-y: auto;

  .list-button {
    cursor: pointer;
    display: block;
    padding: 10px;
    margin-bottom: 15px;
    border: 1px dashed #ddd;
    text-align: center;
  }

  .lookMore {
    font-size: 14px;
    color: #000;
    font-weight: 600;
    color: #8c8e8e;
    text-align: center;
    cursor: pointer;
  }

  .list-chatname {
    display: flex;
    justify-content: space-between;
    font-size: 14px;
    padding: 10px;
    margin-bottom: 10px;
    cursor: pointer;
    border-radius: 8px;

    i {
      margin-right: 5px;
    }

    &:hover {
      // color: #409eff;
      background: #eeeeee;
    }
  }

  .list-name {
    display: flex;
    align-items: center;

    i {
      margin-right: 15px;
    }
  }

  .selected {
    // color: #409eff;
    background: #eeeeee;
  }

  .list-edit {
    display: flex;
    align-items: center;
  }
}

.chat-container {
  position: relative;
  display: flex;
  flex-direction: column;
  background: #fff;
}

.chat-header {
  background-color: #409eff;
  color: white;
  text-align: center;
}

.choseRole {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(218px, 1fr));
  grid-gap: 15px;
  /* 设置行和列之间的间距为 15px */
  grid-template-rows: repeat(auto-fit, minmax(195px, 1fr));
  max-height: calc(100vh - 250px);
  border-radius: 5px;
  overflow-y: auto;
  padding: 0 0 0 15px;
  color: #8c8c8c;
  background-color: #fff;

  .role-wrap {
    margin-right: 15px;
    border: 1px solid #f0f0f0;
    padding: 15px;
    border-radius: 5px;
    cursor: pointer;
  }

  .choseRole::after {
    content: "";
    flex: auto;
  }

  .role {
    font-size: 16px;
    color: #141414;
    white-space: nowrap;
    /* 不换行 */
    overflow: hidden;
    /* 隐藏溢出部分 */
    text-overflow: ellipsis;
    /* 显示省略号 */
  }

  .introduce {
    font-size: 14px;
    padding-bottom: 10px;
    border-bottom: 1px solid #f0f0f0;
    white-space: nowrap;
    /* 不换行 */
    overflow: hidden;
    /* 隐藏溢出部分 */
    text-overflow: ellipsis;
    /* 显示省略号 */
  }

  .explain {
    height: 75px;
    overflow-y: auto;
    font-size: 12px;
    color: #000;
    line-height: 25px;
    display: -webkit-box;
    // -webkit-line-clamp: 3; /* 指定显示的行数 */
    // -webkit-box-orient: vertical; /* 设置为垂直方向 */
    // overflow: hidden;
    // text-overflow: ellipsis;
  }

  ::-webkit-scrollbar {
    width: 0px;
  }
}

::-webkit-scrollbar {
  width: 5px;
}

/* 修改滚动条的轨道颜色 */
::-webkit-scrollbar-track {
  width: 5px;
  background-color: #f1f1f1;
}

/* 修改滚动条的滑块颜色 */
::-webkit-scrollbar-thumb {
  background-color: #ccc;
}

/* 修改滚动条的滑块形状 */
::-webkit-scrollbar-thumb:hover {
  background-color: #555;
}

/* 修改滚动条的角落 */
::-webkit-scrollbar-corner {
  background-color: #f1f1f1;
}

.sendInput {
  position: relative;
  padding: 20px;
  max-width: 80%;
  margin: 30px auto 0;

  .sendButton {
    position: absolute;
    bottom: 30px;
    right: 40px;
  }

  .voiceInput {
    color: #b8b8b8;
    position: absolute;
    bottom: 8px;
    right: 80px;
    cursor: pointer;
  }

  .cosplay {
    color: #b8b8b8;
    position: absolute;
    bottom: 8px;
    right: 50px;
    cursor: pointer;
  }

  .changeModel {
    position: absolute;
    left: 20px;
    top: -20px;
    color: #b8b8b8;
    position: absolute;
    bottom: 8px;
    right: 80px;
    cursor: pointer;
  }
}

.voiceInput2 {
  color: #b8b8b8;
  position: absolute;
  bottom: 8px;
  right: 100px;
  cursor: pointer;
}

.chat-main {
  flex: 1;
  padding: 20px;
  padding-bottom: 120px;
  background: #ffffff;
}

.chat-messages {
  height: calc(100vh - 220px);
  overflow-y: auto;
  font-size: 14px;
  line-height: 25px;
  display: flex;
  flex-direction: column;

  .seeMore {
    text-align: center;
    cursor: pointer;
    color: #409EFF;
  }

  .regenerate {
    cursor: pointer;
    position: absolute;
    right: 10px;
    bottom: 5px;

    .el-icon svg {
      font-size: 28px !important;
      color: red;
    }
  }

}

.suspend {
  position: relative;
  z-index: 10000;
  text-align: center !important;
  cursor: pointer;
  color: #F56C6C;
}

.chat-avatar {
  padding-top: 10px;

  img {
    width: 35px;
    border-radius: 50%;
  }
}

.chat-content {
  position: relative;
  display: flex;
  flex-direction: column;
}

.el-icon svg {
  color: #8c8e8e;
}

pre {
  // background-color: #f4f6f8;
  padding: 10px;
  border-radius: 10px;
}




.chat-message-left {
  width: 50vw;
  display: flex;
  border-radius: 5px;
  margin: 10px 0;
  align-self: flex-start;

  .chat-message-text {
    width: 100%;
    overflow-y: auto;
    width: 50vw;
    min-width: 1024px;
    align-self: flex-start;
    margin-left: 10px;
    border-radius: 5px;
    padding: 0 10px;
    background-color: #f4f6f8;
    word-wrap: break-word;
  }

  .chat-time {
    align-self: flex-start;
    font-size: 12px;
    color: #ccc;
    padding-left: 10px;
  }

  .copy {
    color: #409EFF;
    cursor: pointer;
  }
}

.chat-message-right {
  display: flex;
  max-width: 80%;
  border-radius: 5px;
  margin: 10px 0;
  align-self: flex-end;

  .chat-message-text {
    color: #fff;
    align-self: flex-end;
    margin-right: 10px;
    border-radius: 5px;
    padding: 0 10px;
    background-color: #409EFF;
  }

  .chat-time {
    font-size: 12px;
    color: #ccc;
    padding-right: 10px;
    align-self: flex-end;
  }
}


.chat-input {
  flex: 1;
  margin-right: 10px;
}

.chat-footer {
  position: absolute;
  bottom: 40px;
  width: 80%;
  left: 10%;
  display: flex;
  align-items: center;
  padding: 10px;

  .el-icon-delete {
    font-size: 18px;
    margin-right: 10px;
  }

  ::-webkit-scrollbar {
    display: none;
  }
}

.dialog-footer {
  display: inline-block;
  display: flex;
  align-items: right;
  justify-content: flex-end;
  padding-bottom: 40px;
  padding-right: 20px;
}

.day7::before {
  // content: "最近7天：" !important;
  color: #dddddd;
}

:focus {
  outline: 0;
}

:deep(.el-tabs__nav:first-child::before) {
  font-family: "Microsoft YaHei", Arial, sans-serif;
  font-size: 14px;
  padding-top: 10px;
  padding-left: 10px;
  padding-right: 10px;
  content: "模板：";
  color: #a3a3a3;
}
</style>

<style>
/** 列表样式 **/

code {
  font-size: 14px;
  font-family: '微软雅黑' !important;
}

.markdown ol {
  list-style-type: decimal;
  padding-left: 40px;
}

.markdown ul {
  list-style-type: disc;
  padding-left: 40px;
}

.code-block-header {
  width: 100%;
  background: #000 !important;
  display: flex;
  justify-content: space-between;
  color: #dddddd;
}

.code-block-header__lang,
.code-block-header__copy {
  padding: 5px;
  cursor: pointer;
}

/* .demo-tabs{ */
.el-tabs__item {
  font-size: 14px;
  padding: 0 10px;
}

.el-dialog {
  padding: 0px;
}

.el-dialog__body {
  padding: 16px;
}

/* } */
</style>